package com.jse;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;

public class Refs {

	public final static Lookup lookup=MethodHandles.lookup();
	
	public static ClassLoader loader(){
		var cl = Thread.currentThread().getContextClassLoader();
	    if (cl == null) {cl=Refs.class.getClassLoader();}
	    return cl;
	}
	
	public static boolean isExported(Class<?> type) {
        String packageName = type.getPackage().getName();
        Module module = type.getModule();
        return module.isExported(packageName);
    }
		
	public static Class<?> loadClass(String name){
		try {
			if(Compiler.CLASS.containsKey(name))return Compiler.CLASS.get(name);
			return loader().loadClass(name);
		} catch (ClassNotFoundException e) {
			return null;
		}
	}
	
	public static <T> T getField(Object o,String name,Class<T> rt) {
		try {
			return (T)lookup.findVarHandle(o.getClass(),name,rt).get(o);
		} catch (NoSuchFieldException | IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static Object getPrivateField(Object o,String name,Class<?> rt) {
		try {
			return MethodHandles.privateLookupIn(o.getClass(),lookup).findVarHandle(o.getClass(),name,rt).get(o);
		} catch (NoSuchFieldException | IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static Object getStaticField(Class<?> c,String name,Class<?> rt) {
		try {
			return lookup.findStaticVarHandle(c,name,rt).get();
		} catch (NoSuchFieldException | IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}
	public static VarHandle findPrivateStaticVar(Class<?> c,String name,Class<?> rt) {
		try {
			return MethodHandles.privateLookupIn(c,lookup).findStaticVarHandle(c,name,rt);
		} catch (NoSuchFieldException | IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static MethodHandle findMethod(Object o,String name,MethodType rt) {
		try {
			return lookup.findVirtual(o.getClass(), name,rt);
		} catch (Throwable e) {
			throw new RuntimeException(e);
		}
	}
	public static MethodHandle findStaticMethod(Class<?> c,String name,MethodType rt) {
		try {
			return lookup.findStatic(c, name,rt);
		} catch (NoSuchMethodException | IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static MethodHandle findPrivateMethod(Object o,String name,MethodType rt) {
		try {
			return MethodHandles.privateLookupIn(o.getClass(),lookup).findVirtual(o.getClass(),name,rt);
		} catch (Throwable e) {
			throw new RuntimeException(e);
		}
	}
	public static MethodHandle findPrivateStaticMethod(Class<?> c,String name,MethodType rt) {
		try {
			return MethodHandles.privateLookupIn(c,lookup).findStatic(c,name,rt);
		} catch (Throwable e) {
			throw new RuntimeException(e);
		}
	}
	public static boolean wrapEqPrimitive(Class<?> a,Class<?> b) {
		if(a==b)return true;
		if(a.isPrimitive()){
			if(a==boolean.class&&b==Boolean.class)return true;
			else if(a==char.class&&b==Character.class)return true;
			else if(b.getSuperclass()==Number.class)return true;
		}else if(b==null)return true;
		else if(b.isPrimitive()){
			if(b==boolean.class&&a==Boolean.class)return true;
			else if(b==char.class&&a==Character.class)return true;
			else if(a.getSuperclass()==Number.class)return true;
		}
		return false;
	}
	
	public static MethodHandle findConstructor(Class<?> c,MethodType mt) {
		try {
			return lookup.findConstructor(c,mt);
		} catch (Throwable e) {
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 
	 * @param o 是对象直接获取字段,是类获取的静态字段
	 * @param name 字段名
	 * @return
	 */
	public static Object getField(Object o,String name) {
		try {Field field=null;
			if(o instanceof Class<?> c) {field=c.getDeclaredField(name);}
			else field=o.getClass().getDeclaredField(name);
			field.setAccessible(true);return field.get(o);
		} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static void setField(Object o,String name,Object val) {
		try {Field field=null;
			if(o instanceof Class<?> c) {field=c.getDeclaredField(name);}
			else field=o.getClass().getDeclaredField(name);
			field.setAccessible(true);field.set(o,val);
		} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static Object invokeMethod(Object o,Method m,Object...args) {
		try {
			if(Modifier.isPrivate(m.getModifiers()))m.setAccessible(true);
			if(args==null||args.length==0)return lookup.unreflect(m).invoke(o);
			return lookup.unreflect(m).invokeExact(o,args);
		} catch (Throwable e) {
			throw new RuntimeException(e);
		}
	}
	
	public static Object invokeMethod(Object o,String name,Object...args) {
		try {
			Class c=null;
			if(o instanceof Class<?> x)c=x;
			else c=o.getClass();
			if(args==null)return c.getMethod(name).invoke(o);
			var not=true;
			Method m0=null;
			for (int i = 0; i < c.getDeclaredMethods().length; i++) {
				var m=c.getDeclaredMethods()[i];
				if(m.getName().equals(name)) {
					if(m.getParameterCount()==args.length&&args.length==0){
						if(Modifier.isPrivate(m.getModifiers()))
						m.setAccessible(true);
						return m.invoke(o);
					}else if(m.getParameterCount()==args.length) {
						boolean eq=true;
						for (int j = 0; j < args.length; j++)if(!wrapEqPrimitive(m.getParameterTypes()[j],args[j]!=null?args[j].getClass():null))eq=false;
						if(eq) {not=false;return m.invoke(o,args);}
					}else if(args.length==0&&m.getParameterCount()==1&&m.getParameterTypes()[0]==args.getClass()) {//参数数组并传入空数组情况
						not=false;
						return m.invoke(o,args);
					}else {
						m0=m;
					}
				}
			}
			if(not&&m0==null)throw new NoSuchMethodException(c.getName()+"."+name+"("+Arrays.toString(args)+")");
			if(Modifier.isPrivate(m0.getModifiers()))m0.setAccessible(true);
			return m0.invoke(o, args);
		} catch (NoSuchMethodException | SecurityException | IllegalAccessException | InvocationTargetException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static Object newInstance(String c) {
		return newInstance(loadClass(c));
	}
	
	public static <T> T newInstance(Class<T> c,Object...args) {
		try {
			if(args==null||args.length==0)return c.newInstance();
				for (int i = 0; i < c.getConstructors().length; i++) {
					var cs=c.getConstructors()[i];
					if(args.length==0&&cs.getParameterCount()==1&&cs.getParameterTypes()[0]==args.getClass()) {//参数数组并传入空数组情况
						return (T)cs.newInstance(args);
					}else if(cs.getParameterCount()==args.length) {
						boolean eq=true;
						for (int j = 0; j < args.length; j++)if(!wrapEqPrimitive(cs.getParameterTypes()[j],args[j]!=null?args[j].getClass():null))eq=false;
						if(eq)return (T)cs.newInstance(args);
					}
				}
			return c.getConstructor().newInstance(args);
		} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
			throw new RuntimeException(e);
		}
	}
}
